#include "camera.h"

Camera::Camera(int bufferCount)
{
    img_convert_ctx = NULL;
    fd = -1;
    buffers = NULL;
    width = 0;
    height = 0;
    this->bufferCount = bufferCount;
}
bool Camera::initDev(const char *devName, int width, int height)
{
    struct v4l2_capability cap;
    fd = open(devName, O_RDWR);
    if (-1 == fd)
    {
        printf("Camera::initDev can't open the device\n");
        return false;
    }
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1)
    {
        printf("Camera::initDev can't VIDIOC_QUERYCAP\n");
        return false;
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
    {
        printf("Camera::initDev This device doesn't support V4L2_CAP_VIDEO_CAPTURE\n");
        return false;
    }
    if (!(cap.capabilities & V4L2_CAP_STREAMING))
    {
        printf("Camera::initDev This device doesn't support V4L2_CAP_STREAMING\n");
        return false;
    }

    struct v4l2_format fmt;
    memset(&fmt, 0, sizeof(struct v4l2_format));

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = width;
    fmt.fmt.pix.height = height;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;

    if (ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
    {
        printf("Camera::initDev can't VIDIOC_S_FMT\n");
        return false;
    }
    if (ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
    {
        printf("Camera::initDev can't VIDIOC_G_FMT\n");
        return false;
    }
    this->width = fmt.fmt.pix.width;
    this->height = fmt.fmt.pix.height;
    this->bytesPerLine = fmt.fmt.pix.bytesperline;
#ifdef DEBUG
    printf("fmt.fmt.pix.bytesperline:%d\n", fmt.fmt.pix.bytesperline);
    printf("format:%c%c%c%c\n", (fmt.fmt.pix.pixelformat & 0xff),
           ((fmt.fmt.pix.pixelformat >> 8) & 0xff),
           ((fmt.fmt.pix.pixelformat >> 16) & 0xff),
           ((fmt.fmt.pix.pixelformat >> 24) & 0xff));
#endif
    struct v4l2_requestbuffers req;
    memset(&req, 0, sizeof(struct v4l2_requestbuffers));

    req.count = bufferCount;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1)
    {
        printf("Camera::initDev can't VIDIOC_REQBUFS");
        return false;
    }

    buffers = (struct buffer *)calloc(bufferCount, sizeof(struct buffer));
    for (int i = 0; i < bufferCount; i++)
    {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(struct v4l2_buffer));

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
        if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1)
        {
            printf("Camera::initDev can't VIDIOC_QUERYBUF");
            return false;
        }
        buffers[i].len = buf.length;
        buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
        if (MAP_FAILED == buffers[i].start)
        {
            printf("Camera::initDev mmap failed\n");
            return false;
        }
    }

    for (int i = 0; i < bufferCount; i++)
    {
        struct v4l2_buffer buf;
        memset(&buf, 0, sizeof(buf));

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
        if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) //放入缓存
        {
            printf("Camera::initDev can't VIDIOC_QBUF");
            return false;
        }
    }
#if 1
    int ret = 0;
    AVCodec *codec;
    codec = (AVCodec *)avcodec_find_decoder(AV_CODEC_ID_MJPEG);
    if (!codec) {
        fprintf(stderr, "Could not find MJPEG decoder\n");
        exit(1);
    }
     // Allocate a codec context for the decoder.
    pCodecCtx = avcodec_alloc_context3(codec);
    if (!pCodecCtx) {
        fprintf(stderr, "Could not allocate codec context\n");
        exit(1);
    }

    // Open the codec.
    ret = avcodec_open2(pCodecCtx, codec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open codec: %d\n",  ret);
        exit(1);
    }
    #endif
    return true;
}

bool Camera::startStream()
{
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(fd, VIDIOC_STREAMON, &type) == -1)
    {
        printf("Camera::startStream can't VIDIOC_STREAMON\n");
        return false;
    }
    return true;
}

bool Camera::stopStream()
{
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))
    {
        perror("Camera::stopStream can't VIDIOC_STREAMOFF\n");
        return false;
    }
    return true;
}

int Camera::getWidth()
{
    return width;
}
int Camera::getHeight()
{
    return height;
}

Camera::~Camera()
{
    for (int i = 0; i < bufferCount; i++)
        munmap(buffers[i].start, buffers[i].len);
    free(buffers);
    if(pFrame)
        av_frame_free(&pFrame);
    if(pPacket)
        av_packet_free(&pPacket);
    close(fd);
}
 
bool Camera::readFrame(AVFrame *picDest, AVPixelFormat picFmt, int picWidth, int picHeight)
{
    // return true;   // 直接返回内存没有增长
    struct v4l2_buffer buf;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1)
    {
        printf("Camera::readFrame can't VIDIOC_DQBUF\n");
        return false;
    }

    // 从buffers[buf.index].start, buffers[buf.index].len 读取数据，然后填充packet
    if(!pPacket) {
        pPacket = av_packet_alloc();
        pPacket->size = buffers[buf.index].len;
        pPacket->data = (uint8_t *)av_malloc(pPacket->size);
    }
    memcpy(pPacket->data, buffers[buf.index].start, pPacket->size);

    if(!pFrame) {
        pFrame = av_frame_alloc();
    }
       
 
    int got_picture = 0;
    int ret = 0;
    
        ret = avcodec_send_packet(pCodecCtx, pPacket);
         ret = avcodec_receive_frame(pCodecCtx, pFrame);
        if(ret >= 0) {
            got_picture = 1;
        }else
        {
            got_picture = 0;
        }
    
  
    if (ret < 0)
    {
        printf("Camera::readFrame decode error\n");
        return false;
    }
    if (got_picture)
    {
        if(!img_convert_ctx) {
            img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
                                         picWidth, picHeight, picFmt, SWS_BICUBIC, NULL, NULL, NULL);
        }
        sws_scale(img_convert_ctx, (const uint8_t *const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, picDest->data, picDest->linesize);
    }
  
    // av_packet_free(&pPacket);
    // av_frame_free(&pFrame);
   
 
    if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) //放回缓存
    {
        printf("Camera::readFrame can't VIDIOC_QBUF\n");
        return false;
    }
    return true;
}
